/* Copyright Statement:
 *
 * This software/firmware and related documentation ("MediaTek Software") are
 * protected under relevant copyright laws. The information contained herein
 * is confidential and proprietary to MediaTek Inc. and/or its licensors.
 * Without the prior written permission of MediaTek inc. and/or its licensors,
 * any reproduction, modification, use or disclosure of MediaTek Software,
 * and information contained herein, in whole or in part, shall be strictly prohibited.
 */
/* MediaTek Inc. (C) 2010. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE")
 * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON
 * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
 * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
 * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
 * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
 * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
 * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
 * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
 * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND
 * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
 * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE,
 * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
 * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
 *
 * The following software/firmware and/or related documentation ("MediaTek Software")
 * have been modified by MediaTek Inc. All revisions are subject to any receiver's
 * applicable license agreements with MediaTek Inc.
 */

/* drivers/hwmon/mt6516/amit/stk3a5x.c - stk3a5x ALS/PS driver
 *
 * Author: MingHsien Hsieh <minghsien.hsieh@mediatek.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */
#include "stk3a5x.h"

/******************************************************************************
 * configuration
*******************************************************************************/
static struct i2c_client *stk3a5x_i2c_client = NULL;
static struct stk3a5x_data *stk3a5x_obj = NULL;
int stk3a5x_init_flag = -1;
struct regulator *sensor_vgp2;
/*----------------------------------------------------------------------------*/
#ifdef STK_ALS_MID_FIR
    static void stk3a5x_als_bubble_sort(uint16_t* sort_array, uint8_t size_n);
#endif // STK_ALS_MID_FIR
static int stk3a5x_init_client(struct i2c_client *client);
static void stk3a5x_get_data_polling(struct work_struct *work);
static void stk3a5x_get_data_dri(struct work_struct *work);
int32_t stk3a5x_set_ps_thd(struct stk3a5x_data *alps_data, uint16_t thd_h, uint16_t thd_l);
static int32_t stk3a5x_als_get_data(struct stk3a5x_data *alps_data);
static int32_t stk3a5x_ps_get_data(struct stk3a5x_data *alps_data, uint8_t flag);
static void stk3a5x_dump_reg(struct stk3a5x_data *alps_data);
static int32_t stk3a5x_alps_set_config(struct stk3a5x_data *alps_data, stk3a5x_sensor_type sensor_type, bool en);
static void stk3a5x_power(struct device *dev, unsigned int on);
//static int sensor_get_vgp2_supply(struct device *dev);
//int sensor_vgp2_supply_enable(void);
//int sensor_vgp2_supply_disable(void);

/****************************************************************************************************
* Declaration Initiation Setting
****************************************************************************************************/
stk3a5x_register_table stk3a5x_default_register_table[] =
{
#ifdef STK_PS_MULTI_PERSIST
    {STK3A5X_PSCTRL_REG,            (STK3A5X_PS_PRS4 | STK3A5X_PS_GAIN8 | STK3A5X_PS_IT400),    0xFF},
#else
    {STK3A5X_PSCTRL_REG,            (STK3A5X_PS_PRS1 | STK3A5X_PS_GAIN8 | STK3A5X_PS_IT400),    0xFF},
#endif
    {STK3A5X_ALSCTRL1_REG,          (STK3A5X_ALS_PRS1 | STK3A5X_ALS_GAIN64 | STK3A5X_ALS_IT50), 0xFF},
    {STK3A5X_LEDCTRL_REG,           STK3A5X_LED_25mA,                                           0xFF},
    {STK3A5X_WAIT_REG,              STK3A5X_WAIT20,                                             0xFF},
    {0xDB,                          0x15,                                                       0xFF},
#ifdef STK_PERIOD_REFRESH
    {0x4F,                          STK3A5X_INTELL_20,                                          0xFF},
#endif
    {0xA0,                          0x10,                                                       0x10},
    {0xAA,                          STK3A5X_PS_BGIR_THRESHOLD,                                  0x7F},
    {0xF6,                          0x82,                                                       0xFF},
};

stk3a5x_register_table stk3a5x_default_als_thd_table[] =
{
    {STK3A5X_THDH1_ALS_REG, 0x00, 0xFF},
    {STK3A5X_THDH2_ALS_REG, 0x00, 0xFF},
    {STK3A5X_THDL1_ALS_REG, 0xFF, 0xFF},
    {STK3A5X_THDL2_ALS_REG, 0xFF, 0xFF},
};

stk3a5x_register_table stk3a5x_default_ps_thd_table[] =
{
    {STK3A5X_THDH1_PS_REG,  0x00, 0xFF},
    {STK3A5X_THDH2_PS_REG,  0x00, 0xFF},
    {STK3A5X_THDL1_PS_REG,  0xFF, 0xFF},
    {STK3A5X_THDL2_PS_REG,  0xFF, 0xFF},
};

stk3a5x_register_table stk3a5x_default_otp_table[] =
{
    {0x93,  0x01,   0x01},
    {0x90,  0x11,   0x3f},
    {0x92,  0x01,   0x02},
    {0x93,  0x00,   0x01},
};

uint8_t stk3a5x_pid_list[STK3A5X_PID_LIST_NUM] = {0x51, 0x52, 0x53, 0x54, 0x58};

/****************************************************************************************************
* System Setting
****************************************************************************************************/
int32_t stk3a5x_request_registry(struct stk3a5x_data *alps_data)
{
    int32_t err = 0;
    uint32_t FILE_SIZE = sizeof(stk3a5x_cali_table);
    uint8_t file_out_buf[FILE_SIZE];
    struct file  *cali_file = NULL;
    mm_segment_t fs;
    loff_t pos;
    return 0; //must fix
    printk(KERN_ERR "%s:start\n", __func__);
    memset(file_out_buf, 0, FILE_SIZE);
    cali_file = filp_open(STK3A5X_CALI_FILE, O_RDONLY, 0);

    if (IS_ERR(cali_file))
    {
        err = PTR_ERR(cali_file);
        printk(KERN_ERR "%s: filp_open error!err=%d,path=%s\n", __func__, err, STK3A5X_CALI_FILE);
        return -1;
    }
    else
    {
        fs = get_fs();
        set_fs(KERNEL_DS);
        pos = 0;
        vfs_read(cali_file, file_out_buf, sizeof(file_out_buf), &pos);
        set_fs(fs);
    }

    filp_close(cali_file, NULL);
    memcpy(&alps_data->cali_info.cali_para.als_version, file_out_buf, FILE_SIZE);
    printk(KERN_ERR "%s:als_version = 0x%X, als_scale = 0x%X\n",
           __func__,
           alps_data->cali_info.cali_para.als_version,
           alps_data->cali_info.cali_para.als_scale);
    printk(KERN_ERR "%s:ps_version = 0x%X, ps_near = 0x%X, ps_far = 0x%X, ps_ctir = 0x%X\n",
           __func__,
           alps_data->cali_info.cali_para.ps_version,
           alps_data->cali_info.cali_para.ps_near_thd,
           alps_data->cali_info.cali_para.ps_far_thd,
           alps_data->cali_info.cali_para.ps_ctir);
    return 0;
}

void stk3a5x_get_reg_default_setting(uint8_t reg, uint16_t* value)
{
    uint16_t reg_count = 0, reg_num = sizeof(stk3a5x_default_register_table) / sizeof(stk3a5x_register_table);

    for (reg_count = 0; reg_count < reg_num; reg_count++)
    {
        if (stk3a5x_default_register_table[reg_count].address == reg)
        {
            *value = (uint16_t)stk3a5x_default_register_table[reg_count].value;
            return;
        }
    }

    *value = 0x7FFF;
}

static void stk3a5x_start_timer(struct stk3a5x_data *alps_data, stk3a5x_timer_type timer_type)
{
    switch (timer_type)
    {
#ifdef STK_TUNE0

        case STK3A5X_TUNE0_TIMER:
            if (alps_data->tune0_timer_info.timer_is_exist)
            {
                if (alps_data->tune0_timer_info.timer_is_active)
                {
                    APS_LOG("STK3A5X_TUNE0_TIMER was already running\n");
                }
                else
                {
                    hrtimer_start(&alps_data->ps_tune0_timer, alps_data->ps_tune0_delay, HRTIMER_MODE_REL);
                    alps_data->tune0_timer_info.timer_is_active = true;
                }
            }

            break;
#endif

        case STK3A5X_DATA_TIMER_ALPS:
            if (alps_data->alps_timer_info.timer_is_exist)
            {
                if (alps_data->alps_timer_info.timer_is_active)
                {
                    APS_LOG("STK3A5X_DATA_TIMER_ALPS was already running\n");
                }
                else
                {
                    hrtimer_start(&alps_data->alps_timer, alps_data->alps_poll_delay, HRTIMER_MODE_REL);
                    alps_data->alps_timer_info.timer_is_active = true;
                }
            }

            break;

#ifdef STK_ALS_SKIP
        
        case STK3A5X_SKIP_TIMER_ALS:
            if (alps_data->als_skip_timer_info.timer_is_exist)
            {
                if (alps_data->als_skip_timer_info.timer_is_active)
                {
                    APS_LOG("STK3A5X_SKIP_TIMER_ALS was already running\n");
                }
                else
                {
                    hrtimer_start(&alps_data->als_skip_timer, alps_data->als_skip_delay, HRTIMER_MODE_REL);
                    alps_data->als_skip_timer_info.timer_is_active = true;
                }
            }

            break;
#endif

        case STK3A5X_BOOT_REPORT_TIMER_PS:
            break;
    }
}

static void stk3a5x_stop_timer(struct stk3a5x_data *alps_data, stk3a5x_timer_type timer_type)
{
    switch (timer_type)
    {
#ifdef STK_TUNE0

        case STK3A5X_TUNE0_TIMER:
            if (alps_data->tune0_timer_info.timer_is_exist)
            {
                if (alps_data->tune0_timer_info.timer_is_active)
                {
                    APS_LOG("STK3A5X_TUNE0_TIMER stop\n");
                    hrtimer_cancel(&alps_data->ps_tune0_timer);
                    alps_data->tune0_timer_info.timer_is_active = false;
                }
                else
                {
                    APS_LOG("STK3A5X_TUNE0_TIMER was already stop\n");
                }
            }

            break;
#endif

        case STK3A5X_DATA_TIMER_ALPS:
            if (alps_data->alps_timer_info.timer_is_exist)
            {
                if (alps_data->alps_timer_info.timer_is_active)
                {
                    APS_LOG("STK3A5X_DATA_TIMER_ALPS stop\n");
                    hrtimer_cancel(&alps_data->alps_timer);
                    alps_data->alps_timer_info.timer_is_active = false;
                }
                else
                {
                    APS_LOG("STK3A5X_DATA_TIMER_ALPS was already stop\n");
                }
            }

            break;

#ifdef STK_ALS_SKIP
        
        case STK3A5X_SKIP_TIMER_ALS:
            if (alps_data->als_skip_timer_info.timer_is_exist)
            {
                if (alps_data->als_skip_timer_info.timer_is_active)
                {
                    APS_LOG("STK3A5X_SKIP_TIMER_ALS stop\n");
                    hrtimer_cancel(&alps_data->als_skip_timer);
                    alps_data->als_skip_timer_info.timer_is_active = false;
                }
                else
                {
                    APS_LOG("STK3A5X_SKIP_TIMER_ALS was already stop\n");
                }
            }

            break;
#endif

        case STK3A5X_BOOT_REPORT_TIMER_PS:
            break;
    }
}

#ifdef STK_TUNE0
static enum hrtimer_restart stk_ps_tune0_timer_func(struct hrtimer *timer)
{
    struct stk3a5x_data *alps_data = container_of(timer, struct stk3a5x_data, ps_tune0_timer);
    queue_work(alps_data->stk_ps_tune0_wq, &alps_data->stk_ps_tune0_work);
    hrtimer_forward_now(&alps_data->ps_tune0_timer, alps_data->ps_tune0_delay);
    return HRTIMER_RESTART;
}
#endif

#ifdef STK_ALS_SKIP
static enum hrtimer_restart stk_als_skip_timer_func(struct hrtimer *timer)
{
    struct stk3a5x_data *alps_data = container_of(timer, struct stk3a5x_data, als_skip_timer);
    queue_work(alps_data->stk_als_skip_wq, &alps_data->stk_als_skip_work);
    hrtimer_forward_now(&alps_data->als_skip_timer, alps_data->als_skip_delay);
    return HRTIMER_RESTART;
}
#endif

static enum hrtimer_restart stk_alps_timer_func(struct hrtimer *timer)
{
    struct stk3a5x_data *alps_data = container_of(timer, struct stk3a5x_data, alps_timer);
    queue_work(alps_data->stk_alps_wq, &alps_data->stk_alps_work);
    hrtimer_forward_now(&alps_data->alps_timer, alps_data->alps_poll_delay);
    return HRTIMER_RESTART;
}

static int32_t stk3a5x_register_timer(struct stk3a5x_data *alps_data, stk3a5x_timer_type timer_type)
{
    switch (timer_type)
    {
#ifdef STK_TUNE0

        case STK3A5X_TUNE0_TIMER:
            alps_data->stk_ps_tune0_wq = create_singlethread_workqueue("stk_ps_tune0_wq");
            INIT_WORK(&alps_data->stk_ps_tune0_work, stk3a5x_ps_tune0_timer_thread);
            hrtimer_init(&alps_data->ps_tune0_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
            alps_data->ps_tune0_delay = ns_to_ktime(30 * NSEC_PER_MSEC);
            alps_data->ps_tune0_timer.function = stk_ps_tune0_timer_func;
            alps_data->tune0_timer_info.timer_is_exist = true;
            break;
#endif

        case STK3A5X_DATA_TIMER_ALPS:
            alps_data->stk_alps_wq = create_singlethread_workqueue("stk_alps_wq");
            INIT_WORK(&alps_data->stk_alps_work, stk3a5x_get_data_polling);
            hrtimer_init(&alps_data->alps_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
            alps_data->alps_poll_delay = ns_to_ktime(70 * NSEC_PER_MSEC);
            alps_data->alps_timer.function = stk_alps_timer_func;
            alps_data->alps_timer_info.timer_is_exist   = true;
            break;

#ifdef STK_ALS_SKIP  
        case STK3A5X_SKIP_TIMER_ALS:
            alps_data->stk_als_skip_wq = create_singlethread_workqueue("stk_als skip_wq");
            INIT_WORK(&alps_data->stk_als_skip_work, stk3a5x_als_skip_task);
            hrtimer_init(&alps_data->als_skip_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
            alps_data->als_skip_delay = ns_to_ktime(70 * NSEC_PER_MSEC);
            alps_data->als_skip_timer.function = stk_als_skip_timer_func;
            alps_data->als_skip_timer_info.timer_is_exist   = true;
            break;
#endif

        case STK3A5X_BOOT_REPORT_TIMER_PS:
            break;
    }

    return 0;
}

static irqreturn_t stk_oss_irq_handler(int irq, void *data)
{
	if (stk3a5x_obj == NULL)
        return IRQ_HANDLED;
   
	 schedule_work(&stk3a5x_obj->stk_irq_work);
	 disable_irq_nosync(irq);
    return IRQ_HANDLED;
}

static int stk3a5x_register_interrupt(struct stk3a5x_data *alps_data)
{
	if (alps_data->irq_status.irq_is_exist)
	{
		printk(KERN_ERR "%s: irq alreay regist", __func__);
		return 0;
	}

	alps_data->stk_irq_wq = create_singlethread_workqueue("stk_irq_wq");

	if (alps_data->stk_irq_wq)
	{
		INIT_WORK(&alps_data->stk_irq_work, stk3a5x_get_data_dri);
	}
	else
	{
		APS_ERR("create_singlethread_workqueue error\n");
		return -EPERM;
	}

    /* eint request */
    if (alps_data->irq_node)
    {

        if (request_irq(alps_data->stk_irq, stk_oss_irq_handler, IRQF_TRIGGER_NONE, "ALS-eint", NULL))
        {
            APS_ERR("IRQ LINE NOT AVAILABLE!!\n");            
            cancel_work_sync(&alps_data->stk_irq_work);
            destroy_workqueue(alps_data->stk_irq_wq);
            return -EINVAL;
        }

        alps_data->irq_status.irq_is_exist = true;

	//enable_irq_wake(alps_data->stk_irq);
    }
    else
    {
        APS_ERR("null stk_irq node!!\n");
        return -EINVAL;
    }

    return 0;
}

/******************************************************************************
 * Sysfs attributes
*******************************************************************************/
ssize_t config_show(struct device_driver *ddri, char *buf)
{
    ssize_t res;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    res = scnprintf(buf, PAGE_SIZE, "(%d %d %d %d %d %d)\n",
                    atomic_read(&stk3a5x_obj->i2c_retry),
                    atomic_read(&stk3a5x_obj->als_debounce),
                    atomic_read(&stk3a5x_obj->ps_mask),
                    stk3a5x_obj->ps_info.thd_near,
                    stk3a5x_obj->ps_info.thd_far,
                    atomic_read(&stk3a5x_obj->ps_debounce));
    return res;
}

ssize_t config_store(struct device_driver *ddri, const char *buf, size_t count)
{
    int retry, als_deb, ps_deb, mask, hthres, lthres, err;
    struct i2c_client *client;
    client = stk3a5x_i2c_client;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    if (6 == sscanf(buf, "%d %d %d %d %d %d", &retry, &als_deb, &mask, &hthres, &lthres, &ps_deb))
    {
        atomic_set(&stk3a5x_obj->i2c_retry, retry);
        atomic_set(&stk3a5x_obj->als_debounce, als_deb);
        atomic_set(&stk3a5x_obj->ps_mask, mask);
        stk3a5x_obj->ps_info.thd_near = hthres;
        stk3a5x_obj->ps_info.thd_far  = lthres;
        atomic_set(&stk3a5x_obj->ps_debounce, ps_deb);
        err = stk3a5x_set_ps_thd(stk3a5x_obj, hthres, lthres);

        if (err < 0)
        {
            APS_ERR("write high/low thd error: %d\n", err);
            return err;
        }
    }
    else
    {
        APS_ERR("invalid content: '%s'\n", buf);
    }

    return count;
}

ssize_t trace_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "als:%u ps:%u\n", stk3a5x_obj->als_info.enable, stk3a5x_obj->ps_info.enable);
}

ssize_t trace_store(struct device_driver *ddri, const char *buf, size_t count)
{
    int ret, en;
    stk3a5x_sensor_type sensor_type = STK3A5X_NONE;

    if (!stk3a5x_obj)
    {
        printk(KERN_ERR "stk3a5x_obj is null!!\n");
        return 0;
    }

    if (1 == sscanf(buf, "%d", &en))
    {
        switch (en)
        {
            case 1:
                sensor_type = STK3A5X_ALS;
                break;

            case 2:
                sensor_type = STK3A5X_PS;
                break;

            default:
                printk(KERN_ERR "invalid content: '0x%x'\n", en);
                return 0;
        }

        ret = stk3a5x_alps_set_config(stk3a5x_obj, sensor_type, true);
    }
    else
    {
        printk(KERN_ERR "invalid content: '%s', length = %d\n", buf, (int)count);
        return 0;
    }

    return count;
}

ssize_t als_show(struct device_driver *ddri, char *buf)
{
    int ret;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    if (stk3a5x_obj->hw.polling_mode_als == 0)
    {
        ret = stk3a5x_als_get_data(stk3a5x_obj);

        if (ret < 0)
            return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", ret);
        else
            return scnprintf(buf, PAGE_SIZE, "%u\n", stk3a5x_obj->als_info.last_raw_data[0]);
    }

    return scnprintf(buf, PAGE_SIZE, "%u\n", stk3a5x_obj->als_info.last_raw_data[0]);
}

ssize_t lux_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "%u lux\n", stk3a5x_obj->als_info.last_lux);
}

ssize_t lux_store(struct device_driver *ddri, const char *buf, size_t count)
{
    int cmd = 0;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }
    else if (1 != sscanf(buf, "%x", &cmd))
    {
        APS_ERR("invalid format: '%s'\n", buf);
        return 0;
    }

    stk3a5x_obj->als_info.last_lux = cmd;
    return count;
}

ssize_t ps_show(struct device_driver *ddri, char *buf)
{
    int32_t reg_value;
    uint8_t flag_value;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    reg_value = STK3A5X_REG_READ(stk3a5x_obj, STK3A5X_FLAG_REG);

    if (reg_value < 0)
    {
        APS_ERR("i2c failed\n");
        return -1;
    }

    flag_value = (uint8_t)reg_value;
    stk3a5x_ps_get_data(stk3a5x_obj, flag_value);
    return scnprintf(buf, PAGE_SIZE, "%d\n", stk3a5x_obj->ps_info.last_raw_data);
}

ssize_t distance_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "%d\n", stk3a5x_obj->ps_info.last_nearby);
}

ssize_t send_show(struct device_driver *ddri, char *buf)
{
    return 0;
}

ssize_t send_store(struct device_driver *ddri, const char *buf, size_t count)
{
    int addr, cmd;
    u8 dat;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }
    else if (2 != sscanf(buf, "%x %x", &addr, &cmd))
    {
        APS_ERR("invalid format: '%s'\n", buf);
        return 0;
    }

    dat = (u8)cmd;
    APS_ERR("send(%02X, %02X) = %d\n",
            addr,
            cmd,
            STK3A5X_REG_WRITE(stk3a5x_obj, (u16)addr, dat));
    return count;
}

ssize_t recv_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&stk3a5x_obj->recv_reg));
}

ssize_t recv_store(struct device_driver *ddri, const char *buf, size_t count)
{
    int addr;
    u8 dat;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }
    else if (1 != sscanf(buf, "%x", &addr))
    {
        APS_ERR("invalid format: '%s'\n", buf);
        return 0;
    }

    atomic_set(&stk3a5x_obj->recv_reg, 0);
    dat = STK3A5X_REG_READ(stk3a5x_obj, (u16)addr);
    APS_ERR("recv(%02X) = %d, 0x%02X\n", addr, dat, dat);
    atomic_set(&stk3a5x_obj->recv_reg, dat);
    return count;
}

ssize_t allreg_show(struct device_driver *ddri, char *buf)
{
    int ret = 0;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    stk3a5x_dump_reg(stk3a5x_obj);
    return ret;
}

ssize_t status_show(struct device_driver *ddri, char *buf)
{
    ssize_t len = 0;
    u8 rbuf[2];

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    rbuf[0] = STK3A5X_REG_READ(stk3a5x_obj, STK3A5X_STATE_REG);
    rbuf[1] = STK3A5X_REG_READ(stk3a5x_obj, STK3A5X_FLAG_REG);
    len += scnprintf(buf + len, PAGE_SIZE - len, "[PS_EN=%2X] [ALS_EN=%2X] [WAIT_EN=%2X] [NEAR/FAR=%2X] [FLAG_PSINT=%2X] [FLAG_ALSINT=%2X]\n",
                     rbuf[0] & 0x01, (rbuf[0] & 0x02) >> 1, ((rbuf[0] & 0x04) >> 2), rbuf[1] & 0x01, (rbuf[1] & 0x10) >> 4, (rbuf[1] & 0x20) >> 5);
    return len;
}

#if (defined(STK_ALS_CALI) || defined(STK_PS_CALI))
ssize_t cali_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    stk3a5x_request_registry(stk3a5x_obj);
    return scnprintf(buf, PAGE_SIZE, "als scale = %d (Ver. %d), near_thd = %d, far_thd = %d, ctir = %d (Ver. %d) \n",
                     stk3a5x_obj->cali_info.cali_para.als_scale,
                     stk3a5x_obj->cali_info.cali_para.als_version,
                     stk3a5x_obj->cali_info.cali_para.ps_near_thd,
                     stk3a5x_obj->cali_info.cali_para.ps_far_thd,
                     stk3a5x_obj->cali_info.cali_para.ps_ctir,
                     stk3a5x_obj->cali_info.cali_para.ps_version);
}
#endif
#if (defined(STK_ALS_CALI) || defined(STK_PS_CALI))
ssize_t cali_store(struct device_driver *ddri, const char *buf, size_t count)
{
    int data;

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }
    else if (1 != sscanf(buf, "0x%x", &data))
    {
        APS_ERR("invalid format: '%s'\n", buf);
        return 0;
    }

    if ((0x1 == data) || (0x2 == data))
    {
        stk3a5x_obj->cali_info.cali_sensor = (data - 1);

        if (stk3a5x_obj->cali_info.cali_sensor == STK3A5X_CALI_TIMER_ALS)
        {
            stk3a5x_cali_als(stk3a5x_obj);
        }
        else if (stk3a5x_obj->cali_info.cali_sensor == STK3A5X_CALI_TIMER_PS)
        {
            stk3a5x_cali_ps(stk3a5x_obj);
        }
    }

    return count;
}
#endif

/****************************************************************************************************
* ALS control API
****************************************************************************************************/
static int32_t stk3a5x_set_als_thd(struct stk3a5x_data *alps_data, uint16_t thd_h, uint16_t thd_l)
{
    unsigned char val[4];
    int ret;
    val[0] = (thd_h & 0xFF00) >> 8;
    val[1] = thd_h & 0x00FF;
    val[2] = (thd_l & 0xFF00) >> 8;
    val[3] = thd_l & 0x00FF;
    ret = STK3A5X_REG_WRITE_BLOCK(alps_data, STK3A5X_THDH1_ALS_REG, val, sizeof(val));

    if (ret < 0)
    {
        APS_ERR("fail, ret=%d\n", ret);
    }

    return ret;
}

void stk3a5x_als_set_new_thd(struct stk3a5x_data *alps_data, uint16_t alscode)
{
    uint16_t high_thd, low_thd;
    high_thd = alscode + STK3A5X_ALS_THRESHOLD;
    low_thd  = (alscode > STK3A5X_ALS_THRESHOLD) ? (alscode - STK3A5X_ALS_THRESHOLD) : 0;
    stk3a5x_set_als_thd(alps_data, (uint16_t)high_thd, (uint16_t)low_thd);
}

static uint16_t stk_als_max_min(uint16_t* als_raw_data)
{
    uint8_t loop_count = 0;
    uint16_t als_max = 0, als_min = 0xFFFF;

    for (loop_count = 0; loop_count < STK_CHANNEL_NUMBER; loop_count++)
    {
        if ( *(als_raw_data + loop_count + 1) > als_max )
        {
            als_max = *(als_raw_data + loop_count + 1);
        }

        if ( *(als_raw_data + loop_count + 1) < als_min )
        {
            als_min = *(als_raw_data + loop_count + 1);
        }
    }

    return (als_max - als_min);
}

static void stk_als_check_code(struct stk3a5x_data *alps_data, uint16_t* als_raw_data)
{
    uint16_t average_data = 0, diff_data = 0;
    uint8_t loop_count = 0, channel_count = 0;
    bool is_comp = false;

    if (*als_raw_data < STK_CHANNEL_GOLDEN)
    {
        APS_ERR("ALS raw data too small\n");
        return;
    }

    average_data = (*als_raw_data) / STK_CHANNEL_NUMBER;
    diff_data = stk_als_max_min(als_raw_data);

    if (diff_data < average_data)
    {
        //APS_DBG("ALS max_min is normal\n");
        return;
    }

    for (loop_count = 0; loop_count < STK_CHANNEL_NUMBER; loop_count++)
    {
        if ((average_data > *(als_raw_data + loop_count + 1)) &&
            (average_data - (*(als_raw_data + loop_count + 1))) >= (average_data - STK_CHANNEL_OFFSET))
        {
            is_comp = true;
            channel_count ++;
        }
    }

    if (is_comp)
    {
        *als_raw_data = ((*als_raw_data * STK_CHANNEL_NUMBER) / (STK_CHANNEL_NUMBER - channel_count));
        APS_LOG("als_raw_data = %u\n", *als_raw_data);
    }
}

static int32_t stk3a5x_als_get_data(struct stk3a5x_data *alps_data)
{
    uint8_t  raw_data[10];
    uint16_t als_raw_data[5];
    int      loop_count;
    int      err = 0;
#ifdef STK_ALS_SKIP
    int32_t  als_lux;
#endif
    err = STK3A5X_REG_BLOCK_READ(alps_data, STK3A5X_DATA1_ALS_REG, 10, &raw_data[0]);

    if (err < 0)
    {
        return err;
    }

    for (loop_count = 0; loop_count < (sizeof(als_raw_data) / sizeof(als_raw_data[0])); loop_count++)
    {
        *(als_raw_data + loop_count ) = (*(raw_data + (2 * loop_count)) << 8 | *(raw_data + (2 * loop_count + 1) ));
    }

#ifdef STK_ALS_SKIP
    if (alps_data->als_info.als_skip == true)
    {
        alps_data->als_info.als_skip = false;
        als_lux = als_raw_data[0] * alps_data->als_info.scale / 1000;
        APS_LOG("als skip data = %u \n", als_lux);
        return err;
    }
#endif

    if (alps_data->als_info.is_dri)
    {
        stk3a5x_als_set_new_thd(alps_data, alps_data->als_info.last_raw_data[0]);
    }

    stk_als_check_code(alps_data, als_raw_data);
    memcpy(alps_data->als_info.last_raw_data, als_raw_data, sizeof(alps_data->als_info.last_raw_data));
    alps_data->als_info.last_lux = als_raw_data[0] * alps_data->als_info.scale / 1000;
    APS_LOG("als_raw_data = %u\n", *als_raw_data);
    return err;
}

#ifdef STK_TUNE0
ssize_t psmaxdiff_store(struct device_driver *ddri, const char *buf, size_t count)
{
    unsigned long value = 0;
    int ret;

    if ((ret = kstrtoul(buf, 10, &value)) < 0)
    {
        printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
        return ret;
    }

    stk3a5x_obj->ps_info.max_min_diff = (int)value;
    return count;
}

ssize_t psmaxdiff_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "%d\n", stk3a5x_obj->ps_info.max_min_diff);
}

ssize_t psltnct_store(struct device_driver *ddri, const char *buf, size_t count)
{
    unsigned long value = 0;
    int ret;

    if ((ret = kstrtoul(buf, 10, &value)) < 0)
    {
        printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
        return ret;
    }

    stk3a5x_obj->ps_info.lt_n_ct = (int)value;
    return count;
}

ssize_t psltnct_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "%d\n", stk3a5x_obj->ps_info.lt_n_ct);
}

ssize_t pshtnct_store(struct device_driver *ddri, const char *buf, size_t count)
{
    unsigned long value = 0;
    int ret;

    if ((ret = kstrtoul(buf, 10, &value)) < 0)
    {
        printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
        return ret;
    }

    stk3a5x_obj->ps_info.ht_n_ct = (int)value;
    return count;
}

ssize_t pshtnct_show(struct device_driver *ddri, char *buf)
{
    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return 0;
    }

    return scnprintf(buf, PAGE_SIZE, "%d\n", stk3a5x_obj->ps_info.ht_n_ct);
}
#endif

/******************************************************************************
 * MISC device related
******************************************************************************/
#ifdef misc_dev
static long stk3a5x_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    struct i2c_client *client = (struct i2c_client*)file->private_data;
    struct stk3a5x_data *obj = i2c_get_clientdata(client);
    long err = 0;
    void __user *ptr = (void __user*) arg;
    int dat;
    uint32_t enable;
    int ps_result, ps_cali_result;
    int threshold[2];

    switch (cmd)
    {
        case ALSPS_SET_PS_MODE:
            if (copy_from_user(&enable, ptr, sizeof(enable)))
            {
                err = -EFAULT;
                goto err_out;
            }

            APS_ERR("ps_en:%d\n", enable);
            stk3a5x_alps_set_config(obj, STK3A5X_PS, ((enable == 1) ? true : false));
            break;

        case ALSPS_GET_PS_RAW_DATA:
            dat = obj->ps_info.last_raw_data;
            APS_ERR("ps_raw_data:%d\n", dat);

            if (copy_to_user(ptr, &dat, sizeof(dat)))
            {
                err = -EFAULT;
                goto err_out;
            }

            break;

        case ALSPS_SET_ALS_MODE:
            if (copy_from_user(&enable, ptr, sizeof(enable)))
            {
                err = -EFAULT;
                goto err_out;
            }

            APS_ERR("als_en:%d\n", enable);
            stk3a5x_alps_set_config(obj, STK3A5X_ALS, ((enable == 1) ? true : false));
            break;

        case ALSPS_GET_ALS_RAW_DATA:
            dat = obj->als_info.last_raw_data[0];
            APS_ERR("als_raw_data:%d\n", dat);

            if (copy_to_user(ptr, &dat, sizeof(dat)))
            {
                err = -EFAULT;
                goto err_out;
            }

            break;

        case ALSPS_GET_PS_THRESHOLD_HIGH:
            dat = obj->ps_info.thd_near;
            APS_ERR("ps_high_thd_val:%d\n", dat);

            if (copy_to_user(ptr, &dat, sizeof(dat)))
            {
                err = -EFAULT;
                goto err_out;
            }

            break;

        case ALSPS_GET_PS_THRESHOLD_LOW:
            dat = obj->ps_info.thd_far;
            APS_ERR("ps_low_thd_val:%d\n", dat);

            if (copy_to_user(ptr, &dat, sizeof(dat)))
            {
                err = -EFAULT;
                goto err_out;
            }

            break;

        case ALSPS_GET_PS_TEST_RESULT:
            ps_result = obj->ps_info.last_nearby;
            APS_ERR("ALSPS_GET_PS_TEST_RESULT : ps_result = %d\n", ps_result);

            if (copy_to_user(ptr, &ps_result, sizeof(ps_result)))
            {
                err = -EFAULT;
                goto err_out;
            }

            break;

        case ALSPS_IOCTL_CLR_CALI:
            if (copy_from_user(&dat, ptr, sizeof(dat)))
            {
                err = -EFAULT;
                goto err_out;
            }

            obj->ps_info.thd_near = obj->hw.ps_threshold_high;
            obj->ps_info.thd_far  = obj->hw.ps_threshold_low;
            APS_ERR("ALSPS_IOCTL_CLR_CALI : high:%d low:%d\n",
                    obj->ps_info.thd_near,
                    obj->ps_info.thd_far);
            break;

        case ALSPS_IOCTL_GET_CALI:
#if (defined(STK_ALS_CALI) || defined(STK_PS_CALI))
            obj->cali_info.cali_sensor = STK3A5X_CALI_TIMER_PS;
            stk3a5x_cali_ps(obj);
            ps_cali_result = obj->ps_info.ps_cali_data[0];
            APS_ERR("ALSPS_IOCTL_GET_CALI : ps_cali = %d\n", ps_cali_result);

            if (copy_to_user(ptr, &ps_cali_result, sizeof(ps_cali_result)))
            {
                err = -EFAULT;
                goto err_out;
            }

#endif
            break;

        case ALSPS_IOCTL_SET_CALI:
            if (copy_from_user(&ps_cali_result, ptr, sizeof(ps_cali_result)))
            {
                err = -EFAULT;
                goto err_out;
            }

            obj->ps_info.thd_near = ps_cali_result + STK3A5X_HT_N_CT;
            obj->ps_info.thd_far  = ps_cali_result + STK3A5X_LT_N_CT;
            err = stk3a5x_set_ps_thd(obj, obj->ps_info.thd_near, obj->ps_info.thd_far);

            if (err < 0)
            {
                goto err_out;
            }

            APS_ERR("ALSPS_IOCTL_SET_CALI : obj->ps_cali:%d high:%d low:%d\n",
                    ps_cali_result,
                    obj->ps_info.thd_near,
                    obj->ps_info.thd_far);
            break;

        case ALSPS_SET_PS_THRESHOLD:
            if (copy_from_user(threshold, ptr, sizeof(threshold)))
            {
                err = -EFAULT;
                goto err_out;
            }

            APS_ERR("%s set threshold high: 0x%x, low: 0x%x\n", __func__, threshold[0], threshold[1]);
            obj->ps_info.thd_near = threshold[0];
            obj->ps_info.thd_far  = threshold[1];
            err = stk3a5x_set_ps_thd(obj, obj->ps_info.thd_near, obj->ps_info.thd_far);

            if (err < 0)
            {
                goto err_out;
            }

            break;

        default:
            APS_ERR("not supported = 0x%04x", cmd);
            err = -ENOIOCTLCMD;
            break;
    }

err_out:
    return err;
}

static long compat_stk3a5x_unlocked_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    if (!filp->f_op || !filp->f_op->unlocked_ioctl)
    {
        APS_ERR("compat_ioctl f_op has no f_op->unlocked_ioctl.\n");
        return -ENOTTY;
    }

    return filp->f_op->unlocked_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}

static int stk3a5x_open(struct inode *inode, struct file *file)
{
    file->private_data = stk3a5x_i2c_client;

    if (!file->private_data)
    {
        APS_ERR("null pointer!!\n");
        return -EINVAL;
    }

    return nonseekable_open(inode, file);
}

static int stk3a5x_release(struct inode *inode, struct file *file)
{
    file->private_data = NULL;
    return 0;
}

static const struct file_operations STK3A5X_fops =
{
    .owner = THIS_MODULE,
    .open = stk3a5x_open,
    .release = stk3a5x_release,
    .unlocked_ioctl = stk3a5x_unlocked_ioctl,
#if IS_ENABLED(CONFIG_COMPAT)
    .compat_ioctl = compat_stk3a5x_unlocked_ioctl,
#endif
};

static struct miscdevice STK3A5X_device =
{
    .minor = MISC_DYNAMIC_MINOR,
    .name = "als_ps",
    .fops = &STK3A5X_fops,
};
#endif
/****************************************************************************************************
* Common control API
****************************************************************************************************/
#ifdef STK_ALS_SKIP
void stk3a5x_als_skip_task(struct work_struct *work)
{
    struct stk3a5x_data *alps_data = container_of(work, struct stk3a5x_data, stk_als_skip_work);
    int32_t reg_value;
    int32_t ret = 0;
    uint8_t flag_value;
    reg_value = STK3A5X_REG_READ(alps_data, STK3A5X_FLAG_REG);

    if (reg_value < 0)
    {
        APS_ERR("%s: i2c failed\n",__func__);
        return;
    }

    flag_value = (uint8_t)reg_value;

    if (alps_data->als_info.enable && alps_data->als_info.is_dri)
    {
        if (flag_value & STK3A5X_FLG_ALSDR_MASK)
        {
            stk3a5x_als_get_data(alps_data);
        }
        else
        {
            APS_ERR("ALS data was not ready\n");
        }

        if (alps_data->als_info.als_skip == false)
        {
            stk3a5x_stop_timer(alps_data, STK3A5X_SKIP_TIMER_ALS);
            reg_value = STK3A5X_REG_READ(alps_data, STK3A5X_INTCTRL1_REG);
            flag_value = (uint8_t)reg_value;
            flag_value |= STK3A5X_INT_ALS_MASK;
            stk3a5x_set_als_thd(alps_data, 0x0, 0xFFFF);
            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_INTCTRL1_REG,
                                                flag_value,
                                                0xFF);

            if (ret < 0)
                APS_ERR("fail, ret=%d\n", ret);
        }
    }
}

#endif

static void stk3a5x_get_data_polling(struct work_struct *work)
{
    struct stk3a5x_data *alps_data = container_of(work, struct stk3a5x_data, stk_alps_work);
    int32_t reg_value;
    uint8_t flag_value;
    reg_value = STK3A5X_REG_READ(alps_data, STK3A5X_FLAG_REG);

    if (reg_value < 0)
    {
        APS_ERR("i2c failed\n");
        return;
    }

    flag_value = (uint8_t)reg_value;

    if (alps_data->als_info.enable && !alps_data->als_info.is_dri)
    {
        if (flag_value & STK3A5X_FLG_ALSDR_MASK)
        {
            //APS_DBG("ALS data ready\n");
            stk3a5x_als_get_data(alps_data);
        }
        else
        {
            APS_LOG("ALS data was not ready\n");
        }
    }

    if (alps_data->ps_info.enable && !alps_data->ps_info.is_dri)
    {
        APS_LOG("PS data ready\n");
        stk3a5x_ps_get_data(alps_data, flag_value);
    }
}

int32_t stk3a5x_clr_flag(struct stk3a5x_data *alps_data, uint8_t org_flag_reg, uint8_t clr)
{
    uint8_t w_flag;
    int32_t ret;
    w_flag = org_flag_reg | (STK3A5X_FLG_ALSINT_MASK | STK3A5X_FLG_PSINT_MASK);
    w_flag &= (~clr);
    ret = STK3A5X_REG_WRITE(alps_data, STK3A5X_FLAG_REG, w_flag);

    if (ret < 0)
        APS_ERR("fail, ret=%d\n", ret);

    return ret;
}

static void stk3a5x_get_data_dri(struct work_struct *work)
{
    struct stk3a5x_data *alps_data = container_of(work, struct stk3a5x_data, stk_irq_work);
    uint8_t valid_int_flag = STK3A5X_FLG_ALSINT_MASK | STK3A5X_FLG_PSINT_MASK;
    int32_t err;
    uint8_t flag_reg;
    err = STK3A5X_REG_READ(alps_data, STK3A5X_FLAG_REG);

    if (err < 0)
    {
        APS_ERR("Read flag failed\n");
        goto err_i2c_rw;
    }

    flag_reg = (uint8_t)err;
    err = stk3a5x_clr_flag(alps_data, flag_reg, (flag_reg & valid_int_flag));

    if (err < 0)
        goto err_i2c_rw;

    if (flag_reg & STK3A5X_FLG_ALSINT_MASK)
    {
        stk3a5x_als_get_data(alps_data);
    }

    if (flag_reg & STK3A5X_FLG_PSINT_MASK)
    {
        stk3a5x_ps_get_data(alps_data, flag_reg);
    }

    usleep_range(1000, 2000);
    enable_irq(alps_data->stk_irq);
    return;
err_i2c_rw:
    msleep(30);
    enable_irq(alps_data->stk_irq);
    return;
}

static int als_open_report_data(int open)
{
    printk(KERN_ERR "als_open_report_data \n");
    return 0;
}

static int als_enable_nodata(int en)
{
    int ret = 0;
    APS_ERR("als enable value = %d\n", en);
    printk(KERN_ERR "als_enable_nodata = %d\n", en);

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return -1;
    }

    ret = stk3a5x_alps_set_config(stk3a5x_obj, STK3A5X_ALS, en);

    if (ret)
    {
        APS_ERR("failed!!\n");
        return -1;
    }

    return 0;
}

static int als_set_delay(u64 ns)
{
    return 0;
}

static int als_batch(int flag, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs)
{
    return als_set_delay(samplingPeriodNs);
}

static int als_flush(void)
{
    return als_flush_report();
}

static int als_get_data(int* value, int* status)
{
    int ret = 0;
    //APS_DBG("Start\n");

    if (stk3a5x_obj == NULL)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        ret = -1;
    }
    else
    {
        *value = stk3a5x_obj->als_info.last_lux;
        *status = SENSOR_STATUS_ACCURACY_MEDIUM;
    }

    return ret;
}

static int32_t stk3a5x_enable_als(struct stk3a5x_data *alps_data, bool en)
{
    int32_t ret;
    uint8_t reg_value = 0;
#ifdef STK_PERIOD_REFRESH
    uint8_t time_reg_value = 0;
#endif

    if (alps_data->als_info.enable == en)
    {
        APS_LOG("ALS already set\n");
        return ret;
    }

    ret = STK3A5X_REG_READ(alps_data, STK3A5X_STATE_REG);

    if (ret < 0)
    {
        return ret;
    }
    else
    {
        reg_value = (uint8_t)ret;
    }

    reg_value &= (~(STK3A5X_STATE_EN_WAIT_MASK | STK3A5X_STATE_EN_ALS_MASK));

    if (en)
    {
        reg_value |= STK3A5X_STATE_EN_ALS_MASK;
#ifdef STK_PERIOD_REFRESH

        if (alps_data->ps_info.enable
#ifdef STK_TUNE0r
            || alps_data->ps_info.boot_enable
#endif
#ifdef STK_PS_CALI
            || alps_data->ps_info.cali_enable
#endif
           )
        {
            APS_LOG("Setup wait_time is 1.54ms\n");
            reg_value |= STK3A5X_STATE_EN_WAIT_MASK;
            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_WAIT_REG,
                                                0x0,
                                                0xFF);

            if (ret < 0)
            {
                return ret;
            }
        }

#endif
    }
    else
    {
#ifdef STK_ALS_CALI

        if (alps_data->als_info.cali_enable)
        {
            reg_value |= STK3A5X_STATE_EN_ALS_MASK;
        }

#endif

        if (alps_data->ps_info.enable
#ifdef STK_TUNE0
            || alps_data->ps_info.boot_enable
#endif
#ifdef STK_PS_CALI
            || alps_data->ps_info.cali_enable
#endif
           )
        {
            if ((reg_value & STK3A5X_STATE_EN_ALS_MASK) != STK3A5X_STATE_EN_ALS_MASK)
            {
                reg_value |= STK3A5X_STATE_EN_WAIT_MASK;
            }

#ifdef STK_PERIOD_REFRESH

            if ((reg_value & STK3A5X_STATE_EN_ALS_MASK) == STK3A5X_STATE_EN_ALS_MASK)
            {
                reg_value |= STK3A5X_STATE_EN_WAIT_MASK;
                APS_LOG("Setup wait_time is 1.54ms\n");
                time_reg_value = 0;
            }
            else
            {
                APS_LOG("Setup wait_time is default\n");
                time_reg_value = STK3A5X_WAIT50;
            }

            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_WAIT_REG,
                                                time_reg_value,
                                                0xFF);

            if (ret < 0)
            {
                return ret;
            }

#endif
        }
    }

    ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                        STK3A5X_STATE_REG,
                                        reg_value,
                                        0xFF);

    if (ret < 0)
    {
        return ret;
    }

    alps_data->als_info.enable = en;
#ifdef STK_ALS_SKIP
    alps_data->als_info.als_skip = en;
#endif
    return ret;
}

int32_t stk3a5x_set_ps_thd(struct stk3a5x_data *alps_data, uint16_t thd_h, uint16_t thd_l)
{
    unsigned char val[4];
    int ret;
    val[0] = (thd_h & 0xFF00) >> 8;
    val[1] = thd_h & 0x00FF;
    val[2] = (thd_l & 0xFF00) >> 8;
    val[3] = thd_l & 0x00FF;
    ret = STK3A5X_REG_WRITE_BLOCK(alps_data, STK3A5X_THDH1_PS_REG, val, sizeof(val));

    if (ret < 0)
        APS_ERR("fail, ret=%d\n", ret);

    return ret;
}

/****************************************************************************************************
* PS control API
****************************************************************************************************/
int stk3a5x_ps_val(struct stk3a5x_data *alps_data)
{
    int32_t error = 0;
    uint8_t ps_invalid_flag, bgir_raw_data[4] = {0};
    bool bgir_out_of_range = false;
    uint8_t i;
    error = STK3A5X_REG_READ(alps_data, 0xA7);

    if (error < 0)
    {
        APS_ERR("read i2c (0xA7) error\n");
        return error;
    }

    ps_invalid_flag = (uint8_t)error;
    error = STK3A5X_REG_BLOCK_READ(alps_data, 0x34, 4, &bgir_raw_data[0]);

    if (error < 0)
    {
        APS_ERR("read block i2c (0x34) error\n");
        return error;
    }

    for (i = 0; i < 4; i++)
    {
        if ((*(bgir_raw_data + i) & 0x7F) >= STK3A5X_PS_BGIR_THRESHOLD)
        {
            bgir_out_of_range = true;
            APS_ERR("BGIR invalid, bgir[%d] = 0x%X\n", i, (*(bgir_raw_data + i) & 0x7F));
            break;
        }
    }

    if ( ((ps_invalid_flag >> 5) & 0x1) || bgir_out_of_range)
    {
        error = 0x7FFF0001;
    }

    return error;
}

static int32_t stk3a5x_ps_notify(struct stk3a5x_data *alps_data, uint16_t ps_raw, bool is_valid, uint8_t flag)
{
    alps_data->ps_info.ps_report = false;

    if (is_valid)
    {
        if (flag & STK3A5X_FLG_NF_MASK)
        {
            if (alps_data->ps_info.last_nearby != STK3A5X_PRX_FAR_AWAY)
            {
                alps_data->ps_info.ps_report = true;
            }

            alps_data->ps_info.last_nearby = STK3A5X_PRX_FAR_AWAY;
        }
        else
        {
            if (alps_data->ps_info.last_nearby != STK3A5X_PRX_NEAR_BY)
            {
                alps_data->ps_info.ps_report = true;
            }

            alps_data->ps_info.last_nearby = STK3A5X_PRX_NEAR_BY;
        }
    }
    else
    {
        if (alps_data->ps_info.last_nearby != STK3A5X_PRX_FAR_AWAY)
        {
            alps_data->ps_info.ps_report = true;
        }

        alps_data->ps_info.last_nearby = STK3A5X_PRX_FAR_AWAY;
    }

#ifdef STK_TUNE0

    if (alps_data->ps_info.last_nearby == STK3A5X_PRX_NEAR_BY)
    {
        stk3a5x_ps_smudge_judgement(alps_data, ps_raw);
    }

#endif

    if (alps_data->ps_info.ps_report)
    {
        APS_LOG("NF_flag = %d\n", alps_data->ps_info.last_nearby);
        ps_report_interrupt_data(alps_data->ps_info.last_nearby);
    }

    return 0;
}

static int32_t stk3a5x_ps_get_data(struct stk3a5x_data *alps_data, uint8_t flag)
{
    int32_t err = 0;
    uint8_t reg_value[2];

    if ((flag >> 6) & 0x1)
    {
        err = STK3A5X_REG_BLOCK_READ(alps_data, STK3A5X_DATA1_PS_REG, 2, &reg_value[0]);

        if (err < 0)
        {
            APS_ERR("fail, ret=%d\n", err);
            return err;
        }

        alps_data->ps_info.last_raw_data = (reg_value[0] << 8 | reg_value[1]);
    }

    err = stk3a5x_ps_val(alps_data);

    if (err < 0)
    {
        APS_ERR("fail, ret=%d\n", err);
        return err;
    }
    else
    {
        if (err == 0x7FFF0001) //BGIR failed
        {
            err = stk3a5x_ps_notify(alps_data, 0, false, flag);
        }
        else
        {
            err = stk3a5x_ps_notify(alps_data, alps_data->ps_info.last_raw_data, true, flag);
        }
    }

    return err;
}

#if 1 //Disable proximity sensor
static int ps_open_report_data(int open)
{
    APS_ERR("Start\n");
    return 0;
}
#endif

static int ps_enable_nodata(int en)
{
    int ret = 0;
    APS_ERR("ps enable value = %d\n", en);

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return -1;
    }

    ret = stk3a5x_alps_set_config(stk3a5x_obj, STK3A5X_PS, en);

    if (ret)
    {
        APS_ERR("failed!!\n");
        return -1;
    }

    return 0;
}

#if 1 //Disable proximity sensor
static int ps_set_delay(u64 ns)
{
    return 0;
}
#endif

static int ps_batch(int flag, int64_t samplingPeriodNs, int64_t maxBatchReportLatencyNs)
{
    return 0;
}

#if 1 //Disable proximity sensor
static int ps_flush(void)
{
    return ps_flush_report();
}
#endif

static int ps_get_data(int* value, int* status)
{
    printk(KERN_ERR "ps_get_data\n");

    if (!stk3a5x_obj)
    {
        APS_ERR("stk3a5x_obj is null!!\n");
        return -1;
    }

    *value = stk3a5x_obj->ps_info.last_nearby;
    *status = SENSOR_STATUS_ACCURACY_MEDIUM;
    return 0;
}

static int32_t stk3a5x_enable_ps(struct stk3a5x_data *alps_data, bool en)
{
    int32_t ret = 0;
    uint8_t reg_value = 0;

    if (alps_data->ps_info.enable == en)
    {
        APS_ERR("PS already set\n");
        return ret;
    }

    ret = STK3A5X_REG_READ(alps_data, STK3A5X_STATE_REG);

    if (ret < 0)
    {
        return ret;
    }
    else
    {
        reg_value = (uint8_t)ret;
    }

    reg_value &= (~(STK3A5X_STATE_EN_PS_MASK | STK3A5X_STATE_EN_WAIT_MASK
#ifdef STK_PERIOD_REFRESH
                    | STK3A5X_STATE_EN_INTELL_PRST_MASK
#endif
                   ));

    if (en)
    {
        if (alps_data->als_info.enable
#ifdef STK_ALS_CALI
            || alps_data->als_info.cali_enable
#endif
           )
        {
            reg_value |= STK3A5X_STATE_EN_PS_MASK;
#ifdef STK_PERIOD_REFRESH
            reg_value |= (STK3A5X_STATE_EN_INTELL_PRST_MASK | STK3A5X_STATE_EN_WAIT_MASK);
            APS_ERR("Setup wait_time is 1.54ms\n");
            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_WAIT_REG,
                                                0x0,
                                                0xFF);

            if (ret < 0)
            {
                return ret;
            }

#endif
        }
        else
        {
            reg_value |= (STK3A5X_STATE_EN_WAIT_MASK | STK3A5X_STATE_EN_PS_MASK);
#ifdef STK_PERIOD_REFRESH
            reg_value |= STK3A5X_STATE_EN_INTELL_PRST_MASK;
            APS_ERR("Setup wait_time is 50ms\n");
            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_WAIT_REG,
                                                STK3A5X_WAIT50,
                                                0xFF);

            if (ret < 0)
            {
                return ret;
            }

#endif
        }
    }
    else
    {
        if (false
#ifdef STK_PS_CALI
            || alps_data->ps_info.cali_enable
#endif
           )
        {
            reg_value |= STK3A5X_STATE_EN_PS_MASK;

            if (!alps_data->als_info.enable
#ifdef STK_ALS_CALI
                && !alps_data->als_info.cali_enable
#endif
               )
            {
                reg_value |= STK3A5X_STATE_EN_WAIT_MASK;
                APS_ERR("Setup wait_time is default\n");
                ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                    STK3A5X_WAIT_REG,
                                                    STK3A5X_WAIT50,
                                                    0xFF);

                if (ret < 0)
                {
                    return ret;
                }
            }

#ifdef STK_PERIOD_REFRESH
            reg_value |= (STK3A5X_STATE_EN_INTELL_PRST_MASK | STK3A5X_STATE_EN_WAIT_MASK);

            if (alps_data->als_info.enable)
            {
                APS_ERR("Setup wait_time is 1.54ms\n");
                ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                    STK3A5X_WAIT_REG,
                                                    0x0,
                                                    0xFF);

                if (ret < 0)
                {
                    return ret;
                }
            }

#endif
        }

        alps_data->ps_info.last_nearby              = STK3A5X_PRX_NEAR_BY_UNKNOWN;
        alps_data->ps_info.first_init               = true;
        alps_data->ps_info.last_raw_data            = 0;
#ifdef STK_TUNE0
        stk3a5x_stop_timer(alps_data, STK3A5X_TUNE0_TIMER);
        alps_data->ps_info.psi_set                  = 0xFFFF;
        alps_data->ps_info.tune_zero_init_proc      = true;
        alps_data->ps_info.ps_stat_data[0]          = 0;
        alps_data->ps_info.ps_stat_data[1]          = 0;
        alps_data->ps_info.ps_stat_data[2]          = 9999;
        alps_data->ps_info.data_count               = 0;
        alps_data->ps_info.tune0_done               = false;
        alps_data->ps_info.smudge_update            = false;
        alps_data->ps_info.smudge_count             = 0;
        alps_data->ps_info.bgir_update              = false;
        alps_data->ps_info.psa                      = 0x0;
        alps_data->ps_info.psi                      = 0xFFFF;
#endif
    }

    ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                        STK3A5X_STATE_REG,
                                        reg_value,
                                        0xFF);

    if (ret < 0)
    {
        return ret;
    }

    alps_data->ps_info.enable = en;
    return ret;
}

int32_t stk3a5x_ps_get_data_without_notify(struct stk3a5x_data *alps_data, uint16_t *raw_data)
{
    int32_t err = 0;
    uint8_t flag_value;
    uint8_t reg_value[2];
    err = STK3A5X_REG_READ(alps_data, STK3A5X_FLAG_REG);

    if (err < 0)
    {
        APS_ERR("i2c failed\n");
        return err;
    }

    flag_value = (uint8_t)err;

    if ((flag_value >> 6) & 0x1)
    {
        err = STK3A5X_REG_BLOCK_READ(alps_data, STK3A5X_DATA1_PS_REG, 2, &reg_value[0]);

        if (err < 0)
        {
            APS_ERR("fail, ret=%d\n", err);
            return err;
        }

        *raw_data = (reg_value[0] << 8 | reg_value[1]);
        alps_data->ps_info.last_raw_data =  *raw_data;
    }
    else
    {
        if (alps_data->ps_info.last_raw_data == 0)
        {
            APS_ERR("Unavailable operation\n");
            return STK3A5X_PS_DATA_UNAVAILABLE;
        }
        else
        {
            *raw_data = alps_data->ps_info.last_raw_data;
        }
    }

    return err;
}

/****************************************************************************************************
* Common control API
****************************************************************************************************/
static void stk3a5x_init_para(struct stk3a5x_data *alps_data,
                              stk3a5x_sensor_type sensor_type)
{
    if ((sensor_type == STK3A5X_ALS) || (sensor_type == STK3A5X_ALL))
    {
        memset(&alps_data->als_info, 0, sizeof(alps_data->als_info));
        alps_data->als_info.first_init  = true;
        alps_data->als_info.is_dri      = (alps_data->hw.polling_mode_als == 1) ? false : true;
#ifdef STK_ALS_CALI

        if (alps_data->cali_info.cali_para.als_version != 0)
        {
            alps_data->als_info.scale = alps_data->cali_info.cali_para.als_scale;
        }
        else
#endif
        {
            alps_data->als_info.scale = 1000; //should be fixed
        }

#ifdef STK_ALS_SKIP
        alps_data->als_skip_timer_info.timer_is_exist = false;
        alps_data->als_skip_timer_info.timer_is_active = false;
#endif
        
    }

    if ((sensor_type == STK3A5X_PS) || (sensor_type == STK3A5X_ALL))
    {
        memset(&alps_data->ps_info, 0, sizeof(alps_data->ps_info));
#ifdef STK_PS_CALI

        if (alps_data->cali_info.cali_para.ps_version != 0)
        {
            alps_data->ps_info.thd_near             = STK3A5X_HT_N_CT; //[Modify!!] alps_data->cali_info.cali_para.ps_near_thd;
            alps_data->ps_info.thd_far              = STK3A5X_LT_N_CT; //[Modify!!] alps_data->cali_info.cali_para.ps_far_thd;
        }
        else
#endif
        {
            alps_data->ps_info.thd_near             = alps_data->hw.ps_threshold_high;
            alps_data->ps_info.thd_far              = alps_data->hw.ps_threshold_low;
        }

        alps_data->ps_info.ht_n_ct              = STK3A5X_HT_N_CT;
        alps_data->ps_info.lt_n_ct              = STK3A5X_LT_N_CT;
        alps_data->ps_info.max_min_diff         = STK3A5X_MAX_MIN_DIFF;
        alps_data->ps_info.is_dri               = (alps_data->hw.polling_mode_ps == 1) ? false : true;
        alps_data->ps_info.first_init           = true;
        alps_data->ps_info.last_nearby          = STK3A5X_PRX_NEAR_BY_UNKNOWN;
#ifdef STK_TUNE0
        alps_data->ps_info.psi_set              = 0xFFFF;
        alps_data->ps_info.tune_zero_init_proc  = true;
        alps_data->ps_info.ps_stat_data[0]      = 0;
        alps_data->ps_info.ps_stat_data[1]      = 0;
        alps_data->ps_info.ps_stat_data[2]      = 9999;
        alps_data->ps_info.psa                  = 0x0;
        alps_data->ps_info.psi                  = 0xFFFF;
#endif
#ifdef STK_PS_CALI
        alps_data->ps_info.ps_cali_data[0]      = 0;
        alps_data->ps_info.ps_cali_data[1]      = 0;
        alps_data->ps_info.ps_cali_data[2]      = 9999;
#endif
    }
}

static int32_t stk3a5x_alps_set_config(struct stk3a5x_data *alps_data, stk3a5x_sensor_type sensor_type, bool en)
{
    bool is_irq = false;
    int32_t ret = 0;

    if (alps_data->first_init)
    {
        ret = stk3a5x_request_registry(alps_data);

        if (ret < 0)
            return ret;

        stk3a5x_init_para(alps_data, STK3A5X_ALL);
        alps_data->first_init = false;
    }

    if (sensor_type == STK3A5X_ALS)
    {
        if (alps_data->als_info.enable == en)
        {
            APS_ERR("ALS status is same (%d)", en);
            return 0;
        }

        //To-Do: ODR setting
        if (en && alps_data->als_info.is_dri)
        {
            is_irq = true;
        }
    }

    if (sensor_type == STK3A5X_PS)
    {
        if (alps_data->ps_info.enable == en)
        {
            APS_ERR("PS status is same (%d)", en);
            return 0;
        }

        if (en && alps_data->ps_info.first_init)
        {
            stk3a5x_set_ps_thd(alps_data, alps_data->ps_info.thd_near, alps_data->ps_info.thd_far);
#ifdef STK_TUNE0
            // register tune0 timer
            stk3a5x_register_timer(alps_data, STK3A5X_TUNE0_TIMER);
            stk3a5x_start_timer(alps_data, STK3A5X_TUNE0_TIMER);
#endif
            alps_data->ps_info.first_init = false;
        }

        if (en && alps_data->ps_info.is_dri)
        {
            is_irq = true;
        }
    }

    if ((alps_data->als_info.is_dri | alps_data->ps_info.is_dri) && is_irq)
    {
        if (is_irq && alps_data->als_info.is_dri && (sensor_type == STK3A5X_ALS))
        {
#ifdef STK_ALS_SKIP
            if ((!alps_data->als_skip_timer_info.timer_is_exist) && en)
            {
                APS_LOG("register ALS skip timer");
                stk3a5x_register_timer(alps_data, STK3A5X_SKIP_TIMER_ALS);
                stk3a5x_start_timer(alps_data, STK3A5X_SKIP_TIMER_ALS);
            }
#else
            APS_ERR("ALS DRI SETTING");
            stk3a5x_set_als_thd(alps_data, 0x0, 0xFFFF);
            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_INTCTRL1_REG,
                                                STK3A5X_INT_ALS_MASK,
                                                STK3A5X_INT_ALS_MASK);

            if (ret < 0)
            {
                APS_ERR("ALS set INT fail");
                return ret;
            }
#endif
        }

        if (is_irq && alps_data->ps_info.is_dri && (sensor_type == STK3A5X_PS))
        {
            APS_ERR("PS DRI SETTING");
            stk3a5x_set_ps_thd(alps_data, alps_data->ps_info.thd_near, alps_data->ps_info.thd_far);
            ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                                STK3A5X_INTCTRL1_REG,
                                                0x01,
                                                0x07);

            if (ret < 0)
            {
                APS_ERR("PS set INT fail");
                return ret;
            }
        }

        //stk3a5x_register_interrupt(alps_data);
    }
    else
    {
        if (en)
        {
            APS_ERR("ALPS Timer SETTING");

            if (!alps_data->alps_timer_info.timer_is_exist)
            {
                stk3a5x_register_timer(alps_data, STK3A5X_DATA_TIMER_ALPS);
            }

            if (!alps_data->alps_timer_info.timer_is_active)
            {
                stk3a5x_start_timer(alps_data, STK3A5X_DATA_TIMER_ALPS);
            }
        }
    }

    if (sensor_type == STK3A5X_ALS)
    {
        stk3a5x_enable_als(alps_data, en);
    }

    if (sensor_type == STK3A5X_PS)
    {
        stk3a5x_enable_ps(alps_data, en);
    }

    if (!en &&  !alps_data->als_info.enable &&  !alps_data->ps_info.enable)
    {
        stk3a5x_stop_timer(alps_data, STK3A5X_DATA_TIMER_ALPS);
    }

    //stk3a5x_dump_reg(alps_data);
    return 0;
}

#ifdef factory_dev
/****************************************************************************************************
* MTK factory control API
****************************************************************************************************/
static int stk3a5x_als_factory_enable_sensor(bool enable_disable, int64_t sample_periods_ms)
{
    int err = 0;
    err = als_enable_nodata(enable_disable ? 1 : 0);

    if (err)
    {
        APS_ERR("%s failed\n", enable_disable ? "enable" : "disable");
        return -1;
    }

    err = als_batch(0, sample_periods_ms * 1000000, 0);

    if (err)
    {
        APS_ERR("set_batch failed\n");
        return -1;
    }

    return 0;
}

static int stk3a5x_als_factory_get_data(int32_t *data)
{
    int status;
    return als_get_data(data, &status);
}

static int stk3a5x_als_factory_get_raw_data(int32_t *data)
{
    struct stk3a5x_data *obj = stk3a5x_obj;

    if (!obj)
    {
        APS_ERR("obj is null!!\n");
        return -1;
    }

    *data = obj->als_info.last_raw_data[0];
    return 0;
}

static int stk3a5x_als_factory_enable_calibration(void)
{
    return 0;
}

static int stk3a5x_als_factory_clear_cali(void)
{
    return 0;
}

static int stk3a5x_als_factory_set_cali(int32_t offset)
{
    return 0;
}

static int stk3a5x_als_factory_get_cali(int32_t *offset)
{
    return 0;
}

static int stk3a5x_ps_factory_enable_sensor(bool enable_disable, int64_t sample_periods_ms)
{
    int err = 0;
    err = ps_enable_nodata(enable_disable ? 1 : 0);

    if (err)
    {
        APS_ERR("%s failed\n", enable_disable ? "enable" : "disable");
        return -1;
    }

    err = ps_batch(0, sample_periods_ms * 1000000, 0);

    if (err)
    {
        APS_ERR("%s set_batch failed\n", __func__);
        return -1;
    }

    return err;
}

static int stk3a5x_ps_factory_get_data(int32_t *data)
{
    int err = 0, status = 0;
    err = ps_get_data(data, &status);

    if (err < 0)
        return -1;

    return 0;
}

static int stk3a5x_ps_factory_get_raw_data(int32_t *data)
{
    struct stk3a5x_data *obj = stk3a5x_obj;

    if (!obj)
    {
        APS_ERR("obj is null!!\n");
        return -1;
    }

    *data = obj->ps_info.last_raw_data;
    return 0;
}

static int stk3a5x_ps_factory_enable_calibration(void)
{
    return 0;
}

static int stk3a5x_ps_factory_clear_cali(void)
{
    struct stk3a5x_data *obj = stk3a5x_obj;
    obj->ps_info.fac_cali = 0;
    return 0;
}

static int stk3a5x_ps_factory_set_cali(int32_t offset)
{
    struct stk3a5x_data *obj = stk3a5x_obj;
    obj->ps_info.fac_cali = offset;
    return 0;
}

static int stk3a5x_ps_factory_get_cali(int32_t *offset)
{
    struct stk3a5x_data *obj = stk3a5x_obj;
    *offset = obj->ps_info.fac_cali;
    return 0;
}

static int stk3a5x_ps_factory_set_threashold(int32_t threshold[2])
{
    int err = 0;
    struct stk3a5x_data *obj = stk3a5x_obj;
    APS_LOG("set threshold high: 0x%x, low: 0x%x\n", threshold[0], threshold[1]);
    obj->ps_info.thd_near = threshold[0] + obj->ps_info.fac_cali;
    obj->ps_info.thd_far  = threshold[1] + obj->ps_info.fac_cali;
    err = stk3a5x_set_ps_thd(obj, obj->ps_info.thd_near, obj->ps_info.thd_far);

    if (err < 0)
    {
        APS_ERR("set_psensor_threshold fail\n");
        return -1;
    }

    return 0;
}

static int stk3a5x_ps_factory_get_threashold(int32_t threshold[2])
{
    struct stk3a5x_data *obj = stk3a5x_obj;
    threshold[0] = obj->ps_info.thd_near;
    threshold[1] = obj->ps_info.thd_far;
    return 0;
}
#endif
/****************************************************************************************************
* ALPS setting API
****************************************************************************************************/
static void stk3a5x_dump_reg(struct stk3a5x_data *alps_data)
{
    uint8_t stk3a5x_reg_map[] =
    {
        STK3A5X_STATE_REG,
        STK3A5X_PSCTRL_REG,
        STK3A5X_ALSCTRL1_REG,
        STK3A5X_LEDCTRL_REG,
        STK3A5X_INTCTRL1_REG,
        STK3A5X_WAIT_REG,
        STK3A5X_THDH1_PS_REG,
        STK3A5X_THDH2_PS_REG,
        STK3A5X_THDL1_PS_REG,
        STK3A5X_THDL2_PS_REG,
        STK3A5X_THDH1_ALS_REG,
        STK3A5X_THDH2_ALS_REG,
        STK3A5X_THDL1_ALS_REG,
        STK3A5X_THDL2_ALS_REG,
        STK3A5X_FLAG_REG,
        STK3A5X_DATA1_PS_REG,
        STK3A5X_DATA2_PS_REG,
        STK3A5X_DATA1_ALS_REG,
        STK3A5X_DATA2_ALS_REG,
        STK3A5X_DATA1_C_REG,
        STK3A5X_DATA2_C_REG,
        STK3A5X_DATA1_PS_OFFSET_REG,
        STK3A5X_DATA2_PS_OFFSET_REG,
        STK3A5X_PDT_ID_REG,
        STK3A5X_RSRVD_REG,
        STK3A5X_GAINCTRL_REG,
        0x4F,
        0xFA,
        STK3A5X_SW_RESET_REG
    };
    uint8_t i = 0;
    uint16_t n = sizeof(stk3a5x_reg_map) / sizeof(stk3a5x_reg_map[0]);
    int16_t reg_data;

    for (i = 0; i < n; i++)
    {
        reg_data = STK3A5X_REG_READ(alps_data, stk3a5x_reg_map[i]);

        if (reg_data < 0)
        {
            APS_ERR("fail, ret=%d", reg_data);
            return;
        }
        else
        {
            APS_ERR("Reg[0x%2X] = 0x%2X\n", stk3a5x_reg_map[i], (uint8_t)reg_data);
        }
    }
}

static uint8_t stk3a5x_get_mid(struct stk3a5x_data *alps_data)
{
    int32_t err;
    err = STK3A5X_REG_READ(alps_data, 0xE0);

    if (err < 0)
    {
        APS_ERR("fail, ret=%d\n", err);
        return err;
    }

    return (uint8_t)err;
}

static int32_t stk3a5x_prst_cnt_rst_sel(struct stk3a5x_data *alps_data)
{
    uint8_t reg_value = 0x0;
    int32_t err = 0;

    if (0 == (stk3a5x_get_mid(alps_data) & 0x1))
    {
        reg_value = 0x1;
    }

    err = STK3A5X_REG_READ_MODIFY_WRITE(alps_data, 0xFA, reg_value, 0xFF);

    if (err < 0)
    {
        APS_FUN("write i2c error\n");
        return err;
    }

    return err;
}

static int32_t stk3a5x_init_all_reg(struct stk3a5x_data *alps_data)
{
    int32_t ret = 0;
    uint16_t reg_count = 0, reg_num = sizeof(stk3a5x_default_register_table) / sizeof(stk3a5x_register_table);

    for (reg_count = 0; reg_count < reg_num; reg_count++)
    {
        ret = STK3A5X_REG_READ_MODIFY_WRITE(alps_data,
                                            stk3a5x_default_register_table[reg_count].address,
                                            stk3a5x_default_register_table[reg_count].value,
                                            stk3a5x_default_register_table[reg_count].mask_bit);

        if (ret < 0)
        {
            APS_ERR("write i2c error\n");
            return ret;
        }
    }

    ret = stk3a5x_prst_cnt_rst_sel(alps_data);
    return ret;
}

static int32_t stk3a5x_check_pid(struct stk3a5x_data *alps_data)
{
    uint8_t value;
    uint16_t i = 0, pid_num = (sizeof(stk3a5x_pid_list) / sizeof(stk3a5x_pid_list[0]));
    int err;
    return 0;
    err = STK3A5X_REG_READ(alps_data, STK3A5X_PDT_ID_REG);

    if (err < 0)
    {
        APS_ERR("fail, ret=%d\n", err);
        return err;
    }

    value = (uint8_t)err;

    for (i = 0; i < pid_num; i++)
    {
        if (value == stk3a5x_pid_list[i])
        {
            return 0;
        }
    }

    return -1;
}

static int32_t stk3a5x_software_reset(struct stk3a5x_data *alps_data)
{
    int32_t r;
    r = STK3A5X_REG_WRITE(alps_data, STK3A5X_SW_RESET_REG, 0x0);

    if (r < 0)
    {
        APS_ERR("software reset: read error after reset\n");
        return r;
    }

    usleep_range(13000, 15000);
    return 0;
}

static int stk3a5x_init_client(struct i2c_client *client)
{
    struct stk3a5x_data *alps_data = i2c_get_clientdata(client);
    int err;
    APS_LOG("Satrt\n");
    err = stk3a5x_software_reset(alps_data);

    if (err < 0)
    {
        APS_ERR("software reset error, err=%d", err);
        return err;
    }

    err = stk3a5x_check_pid(alps_data);

    if (err < 0)
    {
        APS_ERR("stk3a5x_check_pid error, err=%d", err);
        return err;
    }

    err = stk3a5x_init_all_reg(alps_data);

    if (err < 0)
    {
        APS_ERR("stk3a5x_init_all_reg error, err=%d", err);
        return err;
    }

    alps_data->first_init = true;
    stk3a5x_dump_reg(alps_data);
    return 0;
}
#ifdef factory_dev
static struct alsps_factory_fops stk3a5x_factory_fops =
{
    .als_enable_sensor = stk3a5x_als_factory_enable_sensor,
    .als_get_data = stk3a5x_als_factory_get_data,
    .als_get_raw_data = stk3a5x_als_factory_get_raw_data,
    .als_enable_calibration = stk3a5x_als_factory_enable_calibration,
    .als_clear_cali = stk3a5x_als_factory_clear_cali,
    .als_set_cali = stk3a5x_als_factory_set_cali,
    .als_get_cali = stk3a5x_als_factory_get_cali,

    .ps_enable_sensor = stk3a5x_ps_factory_enable_sensor,
    .ps_get_data = stk3a5x_ps_factory_get_data,
    .ps_get_raw_data = stk3a5x_ps_factory_get_raw_data,
    .ps_enable_calibration = stk3a5x_ps_factory_enable_calibration,
    .ps_clear_cali = stk3a5x_ps_factory_clear_cali,
    .ps_set_cali = stk3a5x_ps_factory_set_cali,
    .ps_get_cali = stk3a5x_ps_factory_get_cali,
    .ps_set_threshold = stk3a5x_ps_factory_set_threashold,
    .ps_get_threshold = stk3a5x_ps_factory_get_threashold,
};

struct alsps_factory_public stk3a5x_factory_device =
{
    .gain = 1,
    .sensitivity = 1,
    .fops = &stk3a5x_factory_fops,
};
#endif
int stk3a5x_suspend(struct device *dev)
{
    #if 0
	    struct stk3a5x_data *alps_data = dev_get_drvdata(dev);
	    mutex_lock(&alps_data->io_lock);
	    alps_data->als_info.sr_enable   = alps_data->als_info.enable;
	    alps_data->ps_info.sr_enable    = alps_data->ps_info.enable;

	    if (alps_data->als_info.enable)
	    {
		APS_LOG("Disable ALS : 0\n");
		stk3a5x_alps_set_config(alps_data, STK3A5X_ALS, 0);
	    }

	    if (alps_data->ps_info.enable)
	    {
		APS_LOG("Disable PS : 0\n");
		stk3a5x_alps_set_config(alps_data, STK3A5X_PS, 0);
	    }

	    mutex_unlock(&alps_data->io_lock);
	    stk3a5x_power(dev,0);
    #endif	
    return 0;
}

int stk3a5x_resume(struct device *dev)
{
#if 0
    struct stk3a5x_data *alps_data = dev_get_drvdata(dev);

    stk3a5x_power(dev,1);

    mutex_lock(&alps_data->io_lock);

    if (alps_data->als_info.sr_enable)
    {
        APS_LOG("Enable ALS : 1\n");
        stk3a5x_alps_set_config(alps_data, STK3A5X_ALS, 1);
    }

    if (alps_data->ps_info.sr_enable)
    {
        APS_LOG("Enable PS : 1\n");
        stk3a5x_alps_set_config(alps_data, STK3A5X_PS, 1);
    }

    mutex_unlock(&alps_data->io_lock);
#endif
    return 0;
}

int stk3a5x_remove(struct i2c_client *client)
{
    int err;

    if ((err = stk3a5x_delete_attr()))
    {
        APS_ERR("stk3a5x_delete_attr fail: %d\n", err);
    }

#ifdef misc_dev
    misc_deregister(&STK3A5X_device);
#endif
#ifdef factory_dev
    alsps_factory_device_deregister(&stk3a5x_factory_device);
#endif
    stk3a5x_i2c_client = NULL;
    i2c_unregister_device(client);
    kfree(i2c_get_clientdata(client));
    return 0;
}


#if 0
/* get LDO supply */
static int sensor_get_vgp2_supply(struct device *dev)
{
	int ret;
	struct regulator *sensor_vgp2_ldo;

	APS_LOG("[Kernel/ALSPS] %s \n", __func__);

	sensor_vgp2_ldo = devm_regulator_get(dev, "reg-sensor");
	if (IS_ERR(sensor_vgp2_ldo)) {
		ret = PTR_ERR(sensor_vgp2_ldo);
		pr_debug("failed to get reg-vgp2 LDO\n");
		return ret;
	}

	APS_LOG("ALSPS: sensor get supply ok.\n");

	/* get current voltage settings */
	ret = regulator_get_voltage(sensor_vgp2_ldo);
	APS_LOG("VGP2 LDO voltage = %d in KERNEL stage\n", ret);

	sensor_vgp2 = sensor_vgp2_ldo;

	return ret;
}

int sensor_vgp2_supply_enable(void)
{
	int ret;
	unsigned int volt;

	APS_LOG("[Kernel/ALSPS] %s enter\n", __func__);

	if (sensor_vgp2 == NULL)
		return 0;

	APS_LOG("ALSPS: set regulator voltage sensor_vgp2 voltage to 3.3V\n");
	/* set voltage to 3.3 V */
	ret = regulator_set_voltage(sensor_vgp2, 3300000, 3300000);
	if (ret != 0) {
		APS_LOG("ALSPS: sensor failed to set sensor_vgp2 voltage\n");
		return ret;
	}

	/* get voltage settings again */
	volt = regulator_get_voltage(sensor_vgp2);
	if (volt == 3300000)
		APS_LOG("ALSPS: check regulator voltage=3300000 pass!\n");
	else
		APS_LOG("ALSPS: voltage=3300000 fail! (voltage: %d)\n",volt);

	ret = regulator_enable(sensor_vgp2);
	if (ret != 0) {
		APS_LOG("ALSPS: Failed to enable sensor_vgp2\n");
		return ret;
	}

	return ret;
}

int sensor_vgp2_supply_disable(void)
{
	int ret = 0;
	unsigned int isenable;

	if (sensor_vgp2 == NULL)
		return 0;

	/* disable regulator */
	isenable = regulator_is_enabled(sensor_vgp2);

	APS_LOG("ALSPS: sensor query regulator enable status [%d]\n", isenable);

	if (isenable) {
		ret = regulator_disable(sensor_vgp2);
		if (ret != 0) {
			APS_LOG("ALSPS: sensor failed to disable sensor_vgp2\n");
			return ret;
		}
		/* verify */
		isenable = regulator_is_enabled(sensor_vgp2);
		if (!isenable)
			APS_LOG("ALSPS: sensor regulator disable pass\n");
	}

	return ret;
}

static void stk3a5x_power(struct device *dev, unsigned int on)
{	
	//sensor get supply		
	sensor_get_vgp2_supply(dev);	

	if (on == 1) {
		sensor_vgp2_supply_enable();
	} else {
		sensor_vgp2_supply_disable();
	}


}
#endif
static void stk3a5x_power(struct device *dev, unsigned int on)
{
	printk("nothing\n");
}

/*----------------------------------------------------------------------------*/
static int of_get_stk3a5x_platform_data(struct device *dev)
{
	struct device_node *node = NULL;	
	u32 ints[2] = {0, 0};
	int ret;

	node = of_find_compatible_node(NULL, NULL, "mediatek,alsps");
	if (node) {
		/*upstream code*/
		//ints[0] = of_get_named_gpio(node, "deb-gpios", 0);
		if (ints[0] < 0) {
			APS_ERR("debounce gpio not found\n");
		} else{
			ret = of_property_read_u32(node, "debounce", &ints[1]);
			if (ret < 0)
				APS_ERR("debounce time not found\n");
			else
				gpio_set_debounce(ints[0], ints[1]);
			APS_ERR("in[0]:%d, in[1]:%d!!\n", ints[0], ints[1]);
		}
	       
		stk3a5x_obj->stk_irq = irq_of_parse_and_map(node, 0);
		
		APS_ERR("stk3a5x_obj->stk_irq = %d\n", stk3a5x_obj->stk_irq);
		if (!stk3a5x_obj->stk_irq) {
			APS_ERR("irq_of_parse_and_map fail!!\n");
			return -EINVAL;
		}

	} else {
			APS_ERR("get dts gpio node fail!\n");
			return -1;
	}
	return 0;
}
/*----------------------------------------------------------------------------*/

int stk3a5x_probe(struct i2c_client *client, const struct stk3a5x_bus_ops *bops)
{
    struct stk3a5x_data *obj;
    int err = 0;
    struct als_control_path als_ctl = { 0 };
    struct als_data_path als_data = { 0 };
    struct ps_control_path ps_ctl = { 0 };
    struct ps_data_path ps_data = { 0 };
    APS_LOG("driver version: %s\n", DRIVER_VERSION);

    if (!(obj = kzalloc(sizeof(*obj), GFP_KERNEL)))
    {
        err = -ENOMEM;
        goto exit;
    }

    memset(obj, 0, sizeof(*obj));
    stk3a5x_obj = obj;
    // Get config. from device tree
    err = get_alsps_dts_func(client->dev.of_node, &obj->hw);

    if (err < 0)
    {
        APS_ERR("get customization info from dts failed\n");
        goto exit_init_client_failed;
    }


    err = of_get_stk3a5x_platform_data(&client->dev);
    if (err < 0) {
	APS_ERR("of_get_stk3a5x_platform_data fail\n");
	goto exit;
     }

    obj->client     = client;
    obj->dev        = &client->dev;
    obj->bops       = bops;
    i2c_set_clientdata(client, obj);
    stk3a5x_i2c_client  = client;
    obj->irq_node       = client->dev.of_node;
    mutex_init(&obj->io_lock);

    //open ldo power
    stk3a5x_power(&client->dev,1);

    err = stk3a5x_init_client(client);

    if (err < 0)
    {
        APS_ERR("initialize register failed\n");
        goto exit_init_reg_failed;
    }

    if ((!obj->hw.polling_mode_als) | (!obj->hw.polling_mode_ps))
    {
        stk3a5x_register_interrupt(obj);
    }

#ifdef misc_dev
    err = misc_register(&STK3A5X_device);

    if (err)
    {
        APS_ERR("misc_register failed");
        goto exit_misc_error;
    }

#endif
    err = stk3a5x_create_attr();

    if (err)
    {
        APS_ERR("create attribute err = %d\n", err);
        goto exit_create_attr_failed;
    }

#ifdef factory_dev
    err = alsps_factory_device_register(&stk3a5x_factory_device);

    if (err)
    {
        APS_ERR("stk3a5x_factory_device register failed\n");
        goto exit_misc_device_register_failed;
    }

#endif
    als_ctl.is_use_common_factory = false;
    ps_ctl.is_use_common_factory = false;
    als_ctl.open_report_data = als_open_report_data;
    als_ctl.enable_nodata = als_enable_nodata;
    als_ctl.set_delay = als_set_delay;
    als_ctl.batch = als_batch;
    als_ctl.flush = als_flush;
    als_ctl.is_report_input_direct = false;
    als_ctl.is_support_batch = false;
    //als_ctl.is_polling_mode = (obj->hw.polling_mode_als == 0) ? false : true;
    err = als_register_control_path(&als_ctl);

    if (err)
    {
        APS_ERR("als_control register fail = %d\n", err);
        goto exit_sensor_obj_attach_fail;
    }

    als_data.get_data = als_get_data;
    als_data.vender_div = 100;
    err = als_register_data_path(&als_data);

    if (err)
    {
        APS_ERR("als_data register fail = %d\n", err);
        goto exit_sensor_obj_attach_fail;
    }

#if 1 //Disable proximity sensor
    ps_ctl.open_report_data = ps_open_report_data;
    ps_ctl.enable_nodata = ps_enable_nodata;
    ps_ctl.set_delay = ps_set_delay;
    ps_ctl.batch = ps_batch;
    ps_ctl.flush = ps_flush;
    ps_ctl.is_report_input_direct = false;
    ps_ctl.is_support_batch = false;
    //ps_ctl.is_polling_mode = (obj->hw.polling_mode_ps == 0) ? false : true;
    err = ps_register_control_path(&ps_ctl);

    if (err)
    {
        APS_ERR("ps_control register fail = %d\n", err);
        goto exit_sensor_obj_attach_fail;
    }

    ps_data.get_data = ps_get_data;
    ps_data.vender_div = 100;
    err = ps_register_data_path(&ps_data);

    if (err)
    {
        APS_ERR("ps_data register fail = %d\n", err);
        goto exit_sensor_obj_attach_fail;
    }
#endif

    stk3a5x_init_flag = 0;
    return 0;
exit_sensor_obj_attach_fail:
exit_init_client_failed:
#ifdef factory_dev
exit_misc_device_register_failed:
    alsps_factory_device_deregister(&stk3a5x_factory_device);
#endif
exit_create_attr_failed:
#ifdef misc_dev
exit_misc_error:
    misc_deregister(&STK3A5X_device);
#endif
exit_init_reg_failed:
    kfree(obj);
exit:
    obj = NULL;
    stk3a5x_init_flag = -1;
    APS_ERR("%s: err = %d\n", __FUNCTION__, err);
    return err;
}
